package com.green.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.converters.longconverter.LongNumberConverter;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.green.entity.TbExcel;
import com.green.entity.vo.ExcelVO;
import com.green.mapper.TbExcelMapper;
import com.green.service.TbExcelService;
import com.green.service.WriterService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class WriterServiceImpl implements WriterService {

    private final TbExcelService tbExcelService;
    private final TbExcelMapper tbExcelMapper;

    @Override
    public void demo1(HttpServletResponse response) {
        List<TbExcel> list = tbExcelService.list(new LambdaQueryWrapper<TbExcel>().last(" limit 10"));
        List<ExcelVO> voList = BeanUtil.copyToList(list, ExcelVO.class);
        //1.写到本地
//        EasyExcel.write("D:\\demo1.xlsx",  ExcelVO.class)
//                .sheet("sheet1")
//                .doWrite(voList);

        //2.返回给前端下载
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
//        try {
//            //二进制，但是不影响
////            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//            //直接返回文件
//            response.setContentType("application/octet-stream;charset=UTF-8");
//            response.setCharacterEncoding("utf-8");
//            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
//            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
//            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//            EasyExcel.write(response.getOutputStream(), ExcelVO.class)
//                    .sheet("模板")
//                    .doWrite(voList);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }

        //3、设置列宽（ColumnWidth注解）和修复Long类型精度丢失问题
        try {

            //直接返回文件
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), ExcelVO.class)
                    .registerConverter(new LongStringConverter())
                    .sheet("模板")
                    .doWrite(voList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 单个sheet写入多次，数据在多个集合中
     *
     * @param response
     */
    @Override
    public void demo2(HttpServletResponse response) {
        List<TbExcel> list = tbExcelService.list(new LambdaQueryWrapper<TbExcel>().last(" limit 10"));
        List<ExcelVO> voList = BeanUtil.copyToList(list, ExcelVO.class);
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExcelVO.class).registerConverter(new LongStringConverter()).build()) {
            //二进制，但是不影响
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            //直接返回文件
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            WriteSheet sheet = EasyExcel.writerSheet("模板").build();
            for (int i = 0; i < 5; i++) {
                excelWriter.write(voList, sheet);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 多个sheet写入，数据在单个集合中
     *
     * @param response
     */
    @Override
    public void demo3(HttpServletResponse response) {
        List<TbExcel> list = tbExcelService.list(new LambdaQueryWrapper<TbExcel>().last(" limit 1000"));
        List<ExcelVO> voList = BeanUtil.copyToList(list, ExcelVO.class);
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExcelVO.class).registerConverter(new LongStringConverter()).build()) {
            //二进制，但是不影响
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            //直接返回文件
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            Map<LocalDateTime, List<ExcelVO>> collect = voList.stream().collect(Collectors.groupingBy(ExcelVO::getDateTimeCol));
            int index = 0;
            for (LocalDateTime localDateTime : collect.keySet()) {
                WriteSheet sheet = EasyExcel.writerSheet(index, localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).build();
                excelWriter.write(collect.get(localDateTime), sheet);
                index++;
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置样式
     *
     * @param response
     */
    @Override
    public void demo4(HttpServletResponse response) {
        //1、头的策略
        WriteCellStyle headStyle = new WriteCellStyle();
        //背景为红色
        headStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
        //头的字体
        WriteFont headFontStyle = new WriteFont();
        headFontStyle.setFontHeightInPoints((short) 20);
        headStyle.setWriteFont(headFontStyle);

        //2、内容策略
        WriteCellStyle contentStyle = new WriteCellStyle();
        //这里需要指定FillPatternType为SOLID_FOREGROUND不然没效果，头默认了FillPatternType所以不需要
        contentStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        contentStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
        WriteFont contentFont = new WriteFont();
        contentStyle.setWriteFont(contentFont);

        //设置策略，头是头的样式，内容是内容的样式
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);

        List<TbExcel> list = tbExcelService.list(new LambdaQueryWrapper<TbExcel>().last(" limit 10"));
        List<ExcelVO> voList = BeanUtil.copyToList(list, ExcelVO.class);

        try {
            //二进制，但是不影响
//            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            //直接返回文件
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), ExcelVO.class)
                    .sheet("模板")
                    .registerWriteHandler(horizontalCellStyleStrategy)
                    .doWrite(voList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void demo5(HttpServletResponse response) {

    }

    @Override
    public void export1(HttpServletResponse response) {
        long start1 = System.currentTimeMillis();
        // 每次查询10万条，考虑性能，内存
        Integer pageSize = 100000;
        // 线程池大小跟cpu有关，一般是cpu数量 * 2 + 1
        Integer poolSize = 10;
        // 随机文件名
        String fileName = String.valueOf(UUID.randomUUID());
        // 查询数据总数
        Long totalCount = tbExcelMapper.selectCount(null);
        if (totalCount == 0) {
            log.info("没有数据需要导出");
            return; // 如果没有数据，直接返回
        }

        int loopCount = (int) Math.ceil((double) totalCount / pageSize);  // 使用 Math.ceil 计算循环次数
        // 设置
        final CountDownLatch latch = new CountDownLatch(loopCount);
        log.info("要查询的次数===>{}", loopCount);
        ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            // 创建写对象
            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
            for (int i = 0; i < loopCount; i++) {
                final int pageNum = i + 1; // 改为从1开始，直接使用 i + 1 作为页码
                executorService.execute(() -> {
                    long start = System.currentTimeMillis();
                    // 查询数据
                    IPage<TbExcel> data = tbExcelMapper.selectPage(new Page<>(pageNum, pageSize), null);
                    List<TbExcel> records = data.getRecords();

                    log.info("第{}页,查询耗时===>{}", pageNum, System.currentTimeMillis() - start);

                    WriteSheet writeSheet = EasyExcel.writerSheet(pageNum, "第" + pageNum + "页")
                            .head(ExcelVO.class)
                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                            .build();
                    synchronized (excelWriter) {
                        long start2 = System.currentTimeMillis();
                        excelWriter.write(BeanUtil.copyToList(records, ExcelVO.class), writeSheet);
                        log.info("数据写出耗时===》{}", System.currentTimeMillis() - start2);
                    }

                    latch.countDown();
                });
            }
            latch.await();
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            excelWriter.finish();
            outputStream.flush();
            executorService.shutdown(); // 关闭线程池
            outputStream.close();
            log.info("总耗时====》{}", System.currentTimeMillis() - start1);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void export2(HttpServletResponse response) {
//        long start1 = System.currentTimeMillis();
//        Integer pageSize = 100000;
//        Integer poolSize = 10;
//        String fileName = String.valueOf(UUID.randomUUID());
//        // 查询数据总数
//        Long totalCount = excelMapper.selectCount(null);
//        if (totalCount == 0) {
//            log.info("没有数据需要导出");
//            return; // 如果没有数据，直接返回
//        }
//
//        int loopCount = (int) Math.ceil((double) totalCount / pageSize);  // 使用 Math.ceil 计算循环次数
//        // 设置
//        final CountDownLatch latch = new CountDownLatch(loopCount);
//        log.info("要查询的次数===>{}", loopCount);
//        ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
//        OutputStream outputStream = null;
//        try {
//            outputStream = response.getOutputStream();
//            // 创建写对象
//            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
//            for (int i = 0; i < loopCount; i++) {
//                final int pageNum = i + 1; // 改为从1开始，直接使用 i + 1 作为页码
//                Thread.ofVirtual().start(() -> {
//                    long start = System.currentTimeMillis();
//                    // 查询数据
//                    IPage<TbExcel> data = excelMapper.selectPage(new Page<>(pageNum, pageSize), null);
//                    List<TbExcel> records = data.getRecords();
//                    log.info("第{}页,查询耗时===>{}", pageNum, System.currentTimeMillis() - start);
//
//                    WriteSheet writeSheet = EasyExcel.writerSheet(pageNum, "第" + pageNum + "页")
//                            .head(ExcelVO.class)
//                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
//                            .build();
//                    synchronized (excelWriter) {
//                        long start2 = System.currentTimeMillis();
//                        excelWriter.write(BeanUtil.copyToList(records, ExcelVO.class), writeSheet);
//                        log.info("数据写出耗时===》{}", System.currentTimeMillis() - start2);
//                    }
//
//                    latch.countDown();
//                });
//            }
//            latch.await();
//            response.setContentType("application/octet-stream");
//            response.setCharacterEncoding("utf-8");
//            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//            excelWriter.finish();
//            outputStream.flush();
//            executorService.shutdown(); // 关闭线程池
//            outputStream.close();
//            log.info("总耗时====》{}", System.currentTimeMillis() - start1);
//        } catch (IOException | InterruptedException e) {
//            throw new RuntimeException(e);
//        }
    }

    @Override
    public void export3(HttpServletResponse response) {
//        long start1 = System.currentTimeMillis();
//        Integer pageSize = 100000;
//        Integer poolSize = 10;
//        String fileName = String.valueOf(UUID.randomUUID());
//        // 查询数据总数
//        Long totalCount = excelMapper.selectCount(null);
//        if (totalCount == 0) {
//            log.info("没有数据需要导出");
//            return; // 如果没有数据，直接返回
//        }
//
//        int loopCount = (int) Math.ceil((double) totalCount / pageSize);  // 使用 Math.ceil 计算循环次数
//        // 设置
//        final CountDownLatch latch = new CountDownLatch(loopCount);
//        log.info("要查询的次数===>{}", loopCount);
//        ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
//        OutputStream outputStream = null;
//        try {
//            outputStream = response.getOutputStream();
//            // 创建写对象
//            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
//            WriteSheet writeSheet = EasyExcel.writerSheet(1, "第" + 1 + "页")
//                    .head(ExcelVO.class)
//                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
//                    .registerConverter(new LongStringConverter())
//                    .build();
//            for (int i = 0; i < loopCount; i++) {
//                final int pageNum = i + 1; // 改为从1开始，直接使用 i + 1 作为页码
//                Thread.ofVirtual().start(() -> {
//                    long start = System.currentTimeMillis();
//                    // 查询数据
//                    IPage<TbExcel> data = excelMapper.selectPage(new Page<>(pageNum, pageSize), null);
//                    List<TbExcel> records = data.getRecords();
//                    log.info("第{}页,查询耗时===>{}", pageNum, System.currentTimeMillis() - start);
//
//                    synchronized (excelWriter) {
//                        long start2 = System.currentTimeMillis();
//                        excelWriter.write(BeanUtil.copyToList(records, ExcelVO.class), writeSheet);
//                        log.info("数据写出耗时===》{}", System.currentTimeMillis() - start2);
//                    }
//                    latch.countDown();
//                });
//            }
//            latch.await();
//            response.setContentType("application/octet-stream");
//            response.setCharacterEncoding("utf-8");
//            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
//            excelWriter.finish();
//            outputStream.flush();
//            executorService.shutdown(); // 关闭线程池
//            outputStream.close();
//            log.info("总耗时====》{}", System.currentTimeMillis() - start1);
//        } catch (IOException | InterruptedException e) {
//            throw new RuntimeException(e);
//        }
    }
}
